#include <windows.h>
#include <iostream>
#include "gl\gl.h"
#include "gl\glu.h"
#include "gl\glaux.h"
#include "gl\glut.h"
#include "player.h"
#include "math.h"
#include "gameParameters.h"
#include "constants.h"
#include "gameMath.h"
#include "collisionDetection.h"
#include "weapons/weapons.h"

using namespace std;

Player* Player::player = NULL;

void Player::init()
{
	//current player speed in all directions
	frontBackSpeed = leftRightSpeed = upDownSpeed = 0;
	theta = 0;
	lastx = lasty = 0;
	lastMoveX = lastMoveY = lastMoveZ = 0;

	jumpStrength = 0.5;
	maxSpeed = 5;
	acceleration = 0.2;
	deceleration = 0.2;

	isJumping = true;
	size = 0.184;
	bounce = 0;

	position = Vector(80, 288, 16);
}

Player::Player()
{
	init();
}

float Player::getFrontBackSpeed()
{
	return frontBackSpeed;
}

float Player::getLeftRightSpeed()
{
	return leftRightSpeed;
}

float Player::getUpDownSpeed()
{
	return upDownSpeed;
}

bool Player::getIsOnTheGround()
{
	return isOnTheGround;
}

double Player::getBounce()
{
	return bounce;
}
Vector Player::getPosition()
{
	return position;
}

Weapon* Player::getWeapon()
{
	return weapon;
}
void Player::setNumberOfTrianglesToCollide(int numberOfTriangles)
{
	numberOfTrianglesToCollide = numberOfTriangles;
}
void Player::setTrianglesToCollide(Vector* triangles)
{
	this->trianglesToCollide = triangles;
}

//creation of new player (just one player may be created)
Player* Player::createNewPlayer()
{
	if (player == NULL)
		player = new Player();
	
	return player;
}
//player shoots
void Player::shoot()
{
	if(weapon != NULL)
		weapon->shoot();
}
void Player::changeWeapon(short i)
{
	//don't change weapon if player chose the one that is already chosen
	//if (weaponNumber == i)
	//	return;

	//weaponNumber = i;
	if (weapon != NULL)
		weapon->hideWeapon();
}
//handling player movements
void Player::move(short frontBack, short leftRight, short upDown)
{
	bool moves = (frontBackSpeed != 0) || (leftRightSpeed != 0);
	setSpeed(frontBack, leftRight, upDown);
	//move the player physically
	changePosition();
	setCamera();
}

void Player::death()
{
	init();
}

//player jumps :)
//TODO: change the strength of jump depending on
//how long the user presses the space bar
void Player::jump()
{
	if (isOnTheGround)
	{
		isJumping = true;
		upDownSpeed = jumpStrength;
	}
}


//setting the speed of player
void Player::setSpeed(short frontBack, short leftRight, short upDown)
{
	if (isOnTheGround || GameParameters::flyMode)
	{
		if(frontBack == 0)
		{
			if(frontBackSpeed > 0)
			{
				frontBackSpeed -= deceleration;
				if(frontBackSpeed < 0)
					frontBackSpeed = 0;
			}
			else if(frontBackSpeed < 0)
			{
				frontBackSpeed += deceleration;
				if(frontBackSpeed > 0)
					frontBackSpeed = 0;
			}
		}
		else
		{
			if(frontBack == 1)
			{
				if(frontBackSpeed < maxSpeed)
					frontBackSpeed += acceleration;
			}
			else
			{
				if(frontBackSpeed > -maxSpeed)
					frontBackSpeed -= acceleration;
			}
		}
	}

	if (isOnTheGround || GameParameters::flyMode)
		{
		if(leftRight == 0)
		{
			if(leftRightSpeed > 0)
			{
				leftRightSpeed -= deceleration;
				if(leftRightSpeed < 0)
					leftRightSpeed = 0;
			}
			else if(leftRightSpeed < 0)
			{
				leftRightSpeed += deceleration;
				if(leftRightSpeed > 0)
					leftRightSpeed = 0;
			}
		}
		else
		{
			if(leftRight == 1)
			{
				if(leftRightSpeed < maxSpeed)
					leftRightSpeed += acceleration;
			}
			else
			{
				if(leftRightSpeed > -maxSpeed)
					leftRightSpeed -= acceleration;
			}
		}
	}

	if (GameParameters::applyGravity)
	{
		//apply gravity
		if (upDownSpeed > -GameParameters::maxFallingSpeed)
			upDownSpeed -= GameParameters::gravity;
	}
}

void Player::setBounce()
{
	if ((frontBackSpeed || leftRightSpeed) && isOnTheGround)
	{
		bounce += 0.4;
		if (bounce > Constants::PI_TIMES_TWO)
			bounce = 0;
	}
}
//moving the player, i.e. changing its position
void Player::changePosition()
{
	float pom;

	//forward/backward
	if(GameParameters::flyMode)
	{
		position[1] += frontBackSpeed * sin(moveTheta);
		pom = frontBackSpeed * cos(moveTheta);
	}
	else
		pom = frontBackSpeed;

	position[0] += pom * cos(moveAlpha);
	position[2] += pom * sin(moveAlpha);

	//strafe left/right
	position[0] += -leftRightSpeed * sin(moveAlpha);
	position[2] += leftRightSpeed * cos(moveAlpha);

	//up/down
	position[1] += upDownSpeed;

	////if the player falls too low, reset his position
	//if (position[1] < GameParameters::levelOfResettingPlayerPosition)
	//	position[0] = position[1] = position[2] = 0;
}

//moving the camera
void Player::setCamera()
{
	double ax, ay, az, pom;
	ay = sin(theta);
	pom = cos(theta);
	az = pom * sin(alpha);
	ax = pom * cos(alpha);
	gluLookAt(position[0],
			  position[1] /*+ 0.5*size*//* + (sin(bounce/2) - 0.5)/100*/,
			  position[2]/* + (sin(bounce) - 0.5)/50*/,
			  
			  position[0]+ ax,
			  position[1]+ ay/* + (sin(bounce/2) - 0.5)/100*/,
			  position[2]+ az/* + (sin(bounce) - 0.5)/50*/,

			  
			  0, 1, 0);
}

//handling the player turning (evaluating the change in mouse position)
void Player::handleTurning(int x, int y)
{
	int dx, dy;
	dx = GameParameters::screenWidth / 2;
	dy = GameParameters::screenHeight / 2;
	turn((x - lastx) * GameParameters::angle, -(y - lasty) * GameParameters::angle);

	if(x < dx - 200 || x > dx + 200 || y < dy - 200 || y > dy + 200)
	{
		lastx = dx;
		lasty = dy;
		SetCursorPos(dx, dy);
	}
	else
	{
		lastx = x;
		lasty = y;
	}
}

//turning the player
void Player::turn(double kalpha, double ktheta)
{
	alpha += kalpha;
	if(alpha < 0)
		alpha += Constants::PI_TIMES_TWO;
	else if(alpha > Constants::PI_TIMES_TWO)
		alpha -= Constants::PI_TIMES_TWO;
	theta += ktheta;
	if(theta > Constants::PI_DIVIDED_BY_TWO)
		theta = Constants::PI_DIVIDED_BY_TWO;
	else if(theta < -Constants::PI_DIVIDED_BY_TWO)
		theta = -Constants::PI_DIVIDED_BY_TWO;

	if (isOnTheGround || GameParameters::flyMode)
	{
		moveAlpha = alpha;
		moveTheta = theta;
	}
}

//player hits the ground
void Player::hitTheGround(float antigravity)
{
	//player too strong hit the ground - looses some health
	//if (upDownSpeed <= -GameParameters::maxFallingSpeed)
	//	size /= 2;

	isJumping = false;
	isOnTheGround = true;
	upDownSpeed += antigravity;
}
//checking collisions with obstacles
//TODO: there are still some minor problems with the collision detection
void Player::checkCollisions()
{
	Vector offset(0, 0, 0);
	bool bhitTheGround = false;


	//although this code could be much shorter, I decided
	//to split it into two "for" loops for efficiency reasons
	//(fewer coditions to check)
	//at first check collisions with faces
	Vector triangle[3];
	for (long i = 0; i < numberOfTrianglesToCollide; i += 3)
	{
		triangle[0] = trianglesToCollide[i];
		triangle[1] = trianglesToCollide[i+1];
		triangle[2] = trianglesToCollide[i+2];

		offset = sphereTriangleCollision(triangle, position, size);
		////do not allow to dive into the ground
		//TODO: fix this
		//if (offset[1] < 0 && upDownSpeed < 0)
		//{
		//	position[1] += (size/2 - offset[1]);
		//	upDownSpeed = 0;
		//	offset[1] = 0;
		//}
		position += offset;

		//player hit the ground
		if ((offset[1] > 0.001) && (upDownSpeed <= 0)) 
		{
			bhitTheGround = true;
			hitTheGround(-upDownSpeed);
		}

		//player hit the ceiling - reset his y-axis speed
		if(offset[1] < 0 && isJumping)
			upDownSpeed = 0;
	}
	//if no collision with face detected, check triangle edges
	//the rest is almost the same
	if (offset == 0)
	{
		for (long i = 0; i < numberOfTrianglesToCollide; i += 3)
		{
			triangle[0] = trianglesToCollide[i];
			triangle[1] = trianglesToCollide[i+1];
			triangle[2] = trianglesToCollide[i+2];

			offset = sphereTriangleEdgeCollision(triangle, position, size);
		/*	if (offset[1] < 0 && upDownSpeed < 0)
			{
				position[1] += (size/2 - offset[1]);
				upDownSpeed = 0;
				offset[1] = 0;
			}*/
			position += offset;

			if ((offset[1] > 0) && (upDownSpeed <= 0))
			{
				bhitTheGround = true;
				hitTheGround(-upDownSpeed / 4);
			}

			if(offset[1] < 0 && isJumping)
				upDownSpeed = 0;
		}
	}

	if (!bhitTheGround)
		isOnTheGround = false;
	
}


bool Player::isShooting()
{
	return weapon->getIsShooting();
}